package com.freewater.cargotracker.infrastructure.persistence.mybatis.converter;

import com.freewater.cargotracker.domain.model.cargo.*;
import com.freewater.cargotracker.domain.model.customer.Customer;
import com.freewater.cargotracker.domain.model.customer.Role;
import com.freewater.cargotracker.infrastructure.persistence.mybatis.dbobject.CargoPO;
import com.freewater.cargotracker.infrastructure.persistence.mybatis.dbobject.CustomerPO;
import com.freewater.cargotracker.infrastructure.persistence.mybatis.dbobject.LegPO;
import com.github.meixuesong.aggregatepersistence.Aggregate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class CargoConverter {

    public static CargoPO serialize(Aggregate<Cargo> cargoAggregate) {
        if (cargoAggregate == null) {
            return null;
        }
        final Cargo cargo = cargoAggregate.getRoot();
        return serialize(cargo);
    }

    public static CargoPO serialize(Cargo cargo) {
        if (cargo == null) {
            return null;
        }
        CargoPO cargoPO = new CargoPO();
        final Long cargoId = cargo.getId();
        cargoPO.setId(cargoId);
        cargoPO.setTrackingID(cargo.getTrackingId().getIdString());
        cargoPO.setOrigin(LocationConverter.serialize(cargo.getOrigin()));
        cargoPO.setSpecOrigin(LocationConverter.serialize(cargo.getRouteSpecification().getOrigin()));
        cargoPO.setSpecDestination(LocationConverter.serialize(cargo.getRouteSpecification().getDestination()));
        cargoPO.setSpecArrivalDeadline(cargo.getRouteSpecification().getArrivalDeadline());
        Map<String, CustomerPO> customers = new HashMap<>();
        cargo.getCargoCustomers().getCustomers().forEach((role, customer) -> {
            customers.put(role.name(), CustomerConverter.serialize(customer));
        });
        cargoPO.setCustomers(customers);
        final List<Leg> legs = cargo.getItinerary().getLegs();
        final List<LegPO> legPOs = legs.stream()
                .map(leg -> LegConverter.serialize(cargoId, leg))
                .collect(Collectors.toList());
        cargoPO.setLegs(legPOs);
        cargoPO.setSize(cargo.getSize());
        cargoPO.setVersion(cargo.getVersion());
        return cargoPO;
    }

    public static Cargo deserialize(CargoPO cargoPO) {
        if (cargoPO == null) {
            return null;
        }
        Cargo cargo = new Cargo(new TrackingId(cargoPO.getTrackingID()),
                new RouteSpecification(
                        LocationConverter.deserialize(cargoPO.getSpecOrigin()),
                        LocationConverter.deserialize(cargoPO.getSpecDestination()),
                        cargoPO.getSpecArrivalDeadline()));
        cargo.setOrigin(LocationConverter.deserialize(cargoPO.getOrigin()));
        Map<Role, Customer> customers = new HashMap<>();
        cargoPO.getCustomers().forEach((roleName, customerDo) -> {
            final Customer customer = CustomerConverter.deserialize(customerDo);
            final Role role = Role.valueOf(roleName);
            customers.put(role, customer);
        });
        cargo.setCargoCustomers(new CargoCustomers(customers));
        final List<Leg> legs = cargoPO.getLegs().stream()
                .map(LegConverter::deserialize).collect(Collectors.toList());
        cargo.assignToRoute(new Itinerary(legs));
        cargo.setSize(cargoPO.getSize());
        cargo.setId(cargoPO.getId());
        cargo.setVersion(cargoPO.getVersion());
        return cargo;
    }
}
